package com.jwater.core.metrics;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.ToLongFunction;
/**
 * @description:
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public class JvmMetric {
	public static final double kk = 1024 * 1024;

	private static int pid = -1;

	private static int cpuCount = -1;

	private static List<MemoryPoolMXBean> memoryMXBeans;

	private static ThreadMXBean threadBean;

	private static List<GarbageCollectorMXBean> gcMXBeans;

	static {
		memoryMXBeans = ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class);
		threadBean = ManagementFactory.getThreadMXBean();
		gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
	}

	public static int pid() {
		try {
			if (pid != -1) {
				return pid;
			}
			RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
			Field jvm = runtime.getClass().getDeclaredField("jvm");
			jvm.setAccessible(true);
			sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
			Method pidMethod = mgmt.getClass().getDeclaredMethod("getProcessId");
			pidMethod.setAccessible(true);
			pid = (Integer) pidMethod.invoke(mgmt);
			return pid;
		} catch (Exception e) {
			throw new MetricException("get pid error", e);
		}
	}

	public static int cpuCount() {
		if (cpuCount != -1) {
			return cpuCount;
		}
		cpuCount = Runtime.getRuntime().availableProcessors();
		return cpuCount;
	}

	/**
	 * Java堆内存
	 * 
	 * @return
	 */
	public static Map<String, Double> getHeapMemory() {
		Map<String, Double> heapMemory = new HashMap<String, Double>(5);
		for (MemoryPoolMXBean memoryPoolMXBean : memoryMXBeans) {
			if (MemoryType.HEAP.equals(memoryPoolMXBean.getType())) {
				// 正使用的内存
				heapMemory.merge("used", getUsageValue(memoryPoolMXBean, MemoryUsage::getUsed), Double::sum);
				// 已分配的内存
				heapMemory.merge("size", getUsageValue(memoryPoolMXBean, MemoryUsage::getCommitted), Double::sum);
				// JVM可使用的最大内存
				heapMemory.merge("max", getUsageValue(memoryPoolMXBean, MemoryUsage::getMax), Double::sum);
			}
		}
		return heapMemory;
	}

	/**
	 * Java非堆内存
	 * 
	 * @return
	 */
	public static Map<String, Double> getNonHeapMemory() {
		Map<String, Double> heapMemory = new HashMap<String, Double>(5);
		for (MemoryPoolMXBean memoryPoolMXBean : memoryMXBeans) {
			if (MemoryType.NON_HEAP.equals(memoryPoolMXBean.getType())) {
				// 正使用的内存
				heapMemory.merge("used", getUsageValue(memoryPoolMXBean, MemoryUsage::getUsed), Double::sum);
				// 已分配的内存
				heapMemory.merge("size", getUsageValue(memoryPoolMXBean, MemoryUsage::getCommitted), Double::sum);
				// JVM可使用的最大内存
				heapMemory.merge("max", getUsageValue(memoryPoolMXBean, MemoryUsage::getMax), Double::sum);
				if ("Metaspace".equals(memoryPoolMXBean.getName())) {
					// 元空间
					heapMemory.merge("metaspace", getUsageValue(memoryPoolMXBean, MemoryUsage::getUsed), Double::sum);
				}
			}
		}
		return heapMemory;
	}

	private static double getUsageValue(MemoryPoolMXBean memoryPoolMXBean, ToLongFunction<MemoryUsage> getter) {
		MemoryUsage usage = getUsage(memoryPoolMXBean);
		if (usage == null) {
			return Double.NaN;
		}
		return getter.applyAsLong(usage) / kk;
	}

	private static MemoryUsage getUsage(MemoryPoolMXBean memoryPoolMXBean) {
		try {
			return memoryPoolMXBean.getUsage();
		} catch (InternalError e) {
			return null;
		}
	}

	/**
	 * 线程监控数据
	 * 
	 * @return
	 */
	public static Map<String, Integer> getThreadCount() {
		Map<String, Integer> threads = new HashMap<String, Integer>(3);
		// 峰值线程数
		threads.put("peak", threadBean.getPeakThreadCount());
		// 守护线程数
		threads.put("daemon", threadBean.getDaemonThreadCount());
		// 当前活动线程数
		threads.put("live", threadBean.getThreadCount());
		return threads;
	}

	/**
	 * 不同状态的线程数
	 * 
	 * @return
	 */
	public static Map<String, Long> getThreadStateCount() {
		Map<String, Long> threads = new HashMap<String, Long>(Thread.State.values().length);
		for (Thread.State state : Thread.State.values()) {
			long stateCount = getThreadStateCount(threadBean, state);
			threads.put(getStateTagValue(state), stateCount);
		}
		return threads;
	}

	private static long getThreadStateCount(ThreadMXBean threadBean, Thread.State state) {
		return Arrays.stream(threadBean.getThreadInfo(threadBean.getAllThreadIds()))
				.filter(threadInfo -> threadInfo != null && threadInfo.getThreadState() == state).count();
	}

	private static String getStateTagValue(Thread.State state) {
		return state.name().toLowerCase().replace("_", "-");
	}

	/**
	 * gc信息
	 * 
	 * @return
	 */
	public static List<GcInfo> getGcInfo() {
		List<GcInfo> gcInfos = new ArrayList<>();
		int i = 1;
		for (GarbageCollectorMXBean gcMXBean : gcMXBeans) {
			GcInfo gcInfo = new GcInfo();
			gcInfo.index = i;
			gcInfo.gcName = gcMXBean.getName();
			gcInfo.collectionCount = gcMXBean.getCollectionCount();
			gcInfo.collectionTime = gcMXBean.getCollectionTime();
			gcInfos.add(gcInfo);
			i++;
		}
		return gcInfos;
	}
}
